package com.jiejie.bootsm234.utils;

import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECPoint;

import java.io.IOException;
import java.math.BigInteger;

public class SM2Utils {


    /**
     * 加密方式
     * 0 ：c1c2c3
     * 1 ：c1c3c2
     */
    static String encryption = "1";


    //下面的秘钥可以使用generateKeyPair()生成的秘钥内容
    // 国密规范正式私钥
    static String pubk = "04ECDBEEE485DFC39DDC30C373FA012FC5A1DCAB0CA6210300ABE0DEDDB03096462D9902F293031BC1A521F5943DA0E9AB0291BBDD95CACA240D25E51BAB84B520";
    static String prik = "0087D625FED740CFBC748678379FB5C202C6D947E7296683A5F38C4F06E09ED73E";

    //生成随机秘钥对
    public static void generateKeyPair() {
        SM2 sm2 = SM2.Instance();
        AsymmetricCipherKeyPair key = sm2.ecc_key_pair_generator.generateKeyPair();
        ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) key.getPrivate();
        ECPublicKeyParameters ecpub = (ECPublicKeyParameters) key.getPublic();
        BigInteger privateKey = ecpriv.getD();
        ECPoint publicKey = ecpub.getQ();

        System.out.println("公钥: " + ByteUtils.byteToHex(publicKey.getEncoded()));
        System.out.println("私钥: " + ByteUtils.byteToHex(privateKey.toByteArray()));
    }

    //数据加密
    public static String encrypt(byte[] publicKey, byte[] data) throws IOException {
        if (publicKey == null || publicKey.length == 0) {
            return null;
        }

        if (data == null || data.length == 0) {
            return null;
        }

        byte[] source = new byte[data.length];
        System.arraycopy(data, 0, source, 0, data.length);

        Cipher cipher = new Cipher();
        SM2 sm2 = SM2.Instance();
        ECPoint userKey = sm2.ecc_curve.decodePoint(publicKey);

        ECPoint c1 = cipher.Init_enc(sm2, userKey);
        cipher.Encrypt(source);
        byte[] c3 = new byte[32];
        cipher.Dofinal(c3);

//      log.info("C1 " + Util.byteToHex(c1.getEncoded()));
//      log.info("C2 " + Util.byteToHex(source));
//      log.info("C3 " + Util.byteToHex(c3));


        if ("0".equals(encryption)) {
            //C1 C2 C3拼装成加密字串
            return ByteUtils.byteToHex(c1.getEncoded()) + ByteUtils.byteToHex(source) + ByteUtils.byteToHex(c3);
        } else {
            //C1 C3 C2拼装成加密字串
            return ByteUtils.byteToHex(c1.getEncoded()) + ByteUtils.byteToHex(c3) + ByteUtils.byteToHex(source);
        }
    }

    //数据解密
    public static byte[] decrypt(byte[] privateKey, byte[] encryptedData) throws IOException {
        if (privateKey == null || privateKey.length == 0) {
            return null;
        }

        if (encryptedData == null || encryptedData.length == 0) {
            return null;
        }
        //加密字节数组转换为十六进制的字符串 长度变为encryptedData.length * 2
        String data = ByteUtils.byteToHex(encryptedData);
        /***分解加密字串
         * （C1 = C1标志位2位 + C1实体部分128位 = 130）
         * （C3 = C3实体部分64位  = 64）
         * （C2 = encryptedData.length * 2 - C1长度  - C2长度）
         */
        byte[] c1Bytes = ByteUtils.hexToByte(data.substring(0, 130));
        byte[] c2 = null;
        byte[] c3 = null;
        if ("0".equals(encryption)) {
            //C1 C2 C3 解密模式
            int c2Len = encryptedData.length - 97;
            c2 = ByteUtils.hexToByte(data.substring(130, 130 + 2 * c2Len));
            c3 = ByteUtils.hexToByte(data.substring(130 + 2 * c2Len, 194 + 2 * c2Len));
        } else {
            //C1 C3 C2 解密模式
            c3 = ByteUtils.hexToByte(data.substring(130, 130 + 64));
            c2 = ByteUtils.hexToByte(data.substring(194));
        }
        SM2 sm2 = SM2.Instance();
        BigInteger userD = new BigInteger(1, privateKey);

        //通过C1实体字节来生成ECPoint
        ECPoint c1 = sm2.ecc_curve.decodePoint(c1Bytes);
        Cipher cipher = new Cipher();
        cipher.Init_dec(userD, c1);
        cipher.Decrypt(c2);
        cipher.Dofinal(c3);

        //返回解密结果
        return c2;
    }

    /**
     * 加密一个字符串
     *
     * @param plainText
     * @return
     */
    public static String encrypt(String plainText) {
        if (StringUtils.isNotBlank(plainText)) {
            byte[] sourceData = plainText.getBytes();

            try {
                String cipherText = SM2Utils.encrypt(ByteUtils.hexToByte(pubk), sourceData);
                return cipherText;
            } catch (IllegalArgumentException | IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 解密一个字符串
     *
     * @param cipherText
     * @return
     */
    public static String decrypt(String cipherText) {
        try {
            String plainText = new String(SM2Utils.decrypt(ByteUtils.hexToByte(prik), ByteUtils.hexToByte(cipherText)));
            return plainText;
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }


    public static String decryptConcat(String concat) {
        try {
            return new String(SM2Utils.decrypt(ByteUtils.hexToByte(prik), ByteUtils.hexToByte(concat)));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }


    public static void main(String[] args) throws Exception {
        //生成密钥对
        //generateKeyPair();

        String plainText = "Hello Word 杰 @ 4";
        byte[] sourceData = plainText.getBytes();

        String cipherText = SM2Utils.encrypt(ByteUtils.hexToByte(pubk), sourceData);
        System.out.println("加密后数据: " + cipherText);

        plainText = new String(SM2Utils.decrypt(ByteUtils.hexToByte(prik), ByteUtils.hexToByte(cipherText)));
        System.out.println("解密后数据: " + plainText);
    }


}
